﻿using System;
using System.Collections.Generic;
using System.Linq;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Handlers;
using Orchard.Data;
using SchoolPointe.RelatedContent.Models;
using SchoolPointe.RelatedContent.Settings;

namespace SchoolPointe.RelatedContent.Handlers
{
    public class RelatedContentHandler : ContentHandler
    {
        private readonly IRepository<RelatedContentItemPartRecord> _relatedContentRepo;
        private readonly IContentManager _contentManager;

        public RelatedContentHandler(IRepository<RelatedContentPartRecord> repo, IRepository<RelatedContentItemPartRecord> relatedContentRepo, IContentManager contentManager)
        {
            _relatedContentRepo = relatedContentRepo;
            _contentManager = contentManager;
            Filters.Add(StorageFilter.For(repo));
            OnInitializing<RelatedContentPart>(LazyLoadHandlers);
            OnUnpublishing<RelatedContentPart>(UnpublishHandlers);

            OnPublished<ContentPart>((context, part) =>
                {
                    if (_relatedContentRepo.Count(x => x.RelatedItemIdentifier != null && x.RelatedItemIdentifier != "") > 0)
                    {
                        var identity = _contentManager.GetItemMetadata(part).Identity;
                        var equalityComparer = new ContentIdentity.ContentIdentityEqualityComparer();
                        int index = 0;
                        int pageSize = 100;
                        IEnumerable<RelatedContentItemPartRecord> contentToUpdate;
                        do
                        {
                            contentToUpdate = _relatedContentRepo
                                .Fetch(x => x.RelatedItemIdentifier != null && x.RelatedItemIdentifier != "")
                                .Skip(index).Take(pageSize);
                            foreach (var relatedContentItemPartRecord in contentToUpdate)
                            {
                                var targetIdentity = new ContentIdentity(relatedContentItemPartRecord.RelatedItemIdentifier);
                                if (equalityComparer.Equals(identity, targetIdentity))
                                {
                                    relatedContentItemPartRecord.RelatedItem = part.ContentItem.Record;
                                    relatedContentItemPartRecord.RelatedItemIdentifier = null;
                                    _relatedContentRepo.Update(relatedContentItemPartRecord);
                                }
                                index++;
                            }
                        } while (contentToUpdate.Any());
                    }
                });
        }

        private void LazyLoadHandlers(InitializingContentContext context, RelatedContentPart part)
        {
            var settings = new RelatedContentPartSettings(part.TypePartDefinition.Settings);
            var inverseRelationships = settings.Relationships
                .Where(r => !string.IsNullOrEmpty(r.InverseOf))
                .Select(r => new { ContentType = r.InverseOf.Split('.')[0], Identifier = r.InverseOf.Split('.')[1] });
            part.RelatedItems.Loader(() => {
                //avoid transient object error for a new related item that hasn't been persisted
                if (part.Record.Id == 0) return new List<RelatedContentItemPartRecord>();

                var relationships = _relatedContentRepo.Fetch(rc => rc.RelatedContentPartRecord == part.Record);
                if (inverseRelationships.Count() > 0)
                {
                    foreach (var inverse in inverseRelationships) {
                        relationships = relationships.Union(_relatedContentRepo.Fetch(
                            rc => rc.RelatedContentPartRecord.ContentItemRecord.ContentType.Name == inverse.ContentType && 
                                rc.RelationshipIdentifier == inverse.Identifier && 
                                rc.RelatedItem == part.ContentItem.Record));
                    }                    
                }
                return relationships.ToList();
            });
        }

        private void UnpublishHandlers(PublishContentContext context, RelatedContentPart part)
        {
            foreach (var relationship in part.RelatedItems.Value)
            {
                _relatedContentRepo.Delete(relationship);
            }
        }
    }
}